package singleflight

import "sync"

//首先创建call和Group类型
//call代表正在进行中，或已经结束的请求。使用sync.WaitGroup锁避免重入
//Group是singleflight的主数据结构，管理不同的key的请求(call)
type call struct {
	wg sync.WaitGroup
	val interface{}
	err error
}
type Group struct {
	mu sync.Mutex
	m map[string]*call
}

//实现DO方法，Do方法，接收两个参数，第一个参数是key，第二个参数是一个函数fn。Do的作用就是，针对相同的key
//无论Do调用多少次，函数fn都只会被调用一次，等待fn调用结束了，返回返回值或错误
//g.mu是保护Group的成员变量m不被并发读写而加上的锁。为了便于理解DO函数
func (g *Group)Do(key string,fn func()(interface{},error))(interface{},error){
	g.mu.Lock()
	if g.m==nil{
		g.m=make(map[string]*call)
	}
	if c,ok:=g.m[key];ok{//如果当前请求存在的化，将之前的结果返回
		g.mu.Unlock()
		c.wg.Wait()//假如有两个请求同时请求到这个地址，那么就其中一个等待，等待另一个将结果处理完毕返回
		return c.val,c.err
	}
	c:=new(call)//不存在，重新申请一个
	c.wg.Add(1)
	g.m[key]=c
	g.mu.Unlock()

	c.val,c.err=fn()
	c.wg.Done()
	g.mu.Lock()
	delete(g.m,key)
	g.mu.Unlock()
	return c.val,c.err
}